class Vue {
    constructor(obj_instance) {
        // 将传入的数据 保存至vue的 $data中
        this.$data = obj_instance.data
        // 数据劫持
        Observer(this.$data)
        // HTML 解析农办
        Compile(obj_instance.el, this)
    }
}

// 数据劫持
function Observer(data_instance) {
    // 新建一个订阅者数组，用来存储订阅者
    const dependent = new Dependent()
    // 如果没有值或不为对象那么直接返回出去
    if (!data_instance || typeof data_instance !== 'object') return
    Object.keys(data_instance).forEach(key => {
        let value = data_instance[key]
        // 递归，内部属性
        Observer(value)
        Object.defineProperty(data_instance, key, {
            configurable: true,
            enumerable: true,
            get() {
                console.log(`访问了${key}属性 -->  属性值为：${value}`);
                // 把订阅者 存入订阅者数组
                Dependent.temp && dependent.addSub(Dependent.temp)
                return value
            },
            set(newValue) {
                value = newValue
                // 避免添加的是一个对象 无法监听到 所以重复递归
                Observer(newValue)
                console.log(`修改了${key}属性值${value} --> 为${newValue}`);
                dependent.notify()
            },
        })
    })
}

// HTML 模板，替换dom内 
function Compile(element, vm) {
    // 将dom 保存至vm.$el中
    vm.$el = document.querySelector(element)
    // 创建文档碎片
    const fragment = document.createDocumentFragment()
    let child
    while (child = vm.$el.firstChild) {
        fragment.append(child)
    }
    fragment_compile(fragment)

    // 替换文档碎片内容
    function fragment_compile(node) {
        const pattern = /\{\{\s*(\S+)\s*\}\}/
        // 文本类型的 node类型是3
        if (node.nodeType === 3) {
            // 找到匹配成功的节点
            const result_regex = pattern.exec(node.nodeValue)
            const xxxx = node.nodeValue
            if (result_regex) {
                const arr = result_regex[1].split('.')
                const value = arr.reduce((total, current) => total[current], vm.$data)
                node.nodeValue = xxxx.replace(pattern, value)
                // 创建订阅者
                new Watcher(vm, result_regex[1], newValue => {
                    node.nodeValue = xxxx.replace(pattern, newValue)
                });
            }
            // 如果是文本类型的 dom 那么就不需要再查询里面的子节点了
            return
        }
        // v-model 如果是输入框那么绑定
        if (node.nodeType === 1 && node.nodeName === 'INPUT') {
            // 获得输入框的 自定义属性值
            const arr2 = Array.from(node.attributes)
            arr2.forEach(item => {
                if (item.nodeName === 'v-model') {
                    const value = item.nodeValue.split('.').reduce(
                        (total, current) => total[current], vm.$data
                    )
                    node.value = value
                    // 也需要创建订阅者，
                    new Watcher(vm, item.nodeValue, newValue => {
                        node.value = newValue
                    });
                    node.addEventListener('input', e => {
                        // ['more','like']
                        const arr1 = item.nodeValue.split('.')
                        // ['more']
                        const arr2 = arr1.slice(0, arr1.length - 1)
                        // vm.$el.more
                        const final = arr2.reduce(
                            (total, current) => total[current], vm.$data
                        )
                        console.log(final);
                        // vm.el.more.like
                        final[arr1[arr1.length - 1]] = e.target.value

                    })
                }
            })
        }
        // 遍历子属性
        node.childNodes.forEach(child => fragment_compile(child))
    }
    // 将更新后的 文档碎片保存至$el 中
    vm.$el.appendChild(fragment)
}

// 依赖，存储订阅着数组
class Dependent {
    constructor() {
        // 订阅者数组
        this.subscribers = []
    }
    // 添加订阅者方法
    addSub(sub) {
        this.subscribers.push(sub)
    }
    // 通知更新数据
    notify() {
        this.subscribers.forEach(sub => sub.update())
    }
}

// 订阅者
class Watcher {
    constructor(vm, key, callback) {
        this.vm = vm
        this.key = key
        this.callback = callback
        Dependent.temp = this
        // 用来触发get 保存订阅者
        key.split('.').reduce((total, current) => total[current], vm.$data)
        Dependent.temp = null
    }
    update() {
        const value = this.key.split('.').reduce(
            (total, current) => total[current], this.vm.$data
        )
        this.callback(value)
    }
}